package easycloud.ecs.provider.api.snda;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.snda.storage.security.SNDACredentials;
import com.snda.storage.service.CSService;
import com.snda.storage.service.impl.rest.httpclient.RestCSService;
import com.snda.storage.service.model.CSBucket;
import com.snda.storage.service.model.CSObject;
import com.snda.storage.service.model.StorageObject;

import easycloud.ecs.common.domain.Folder;
import easycloud.ecs.common.domain.Item;
import easycloud.ecs.common.domain.Log.LOG_TYPE;
import easycloud.ecs.common.domain.Provider;
import easycloud.ecs.common.domain.Sync;
import easycloud.ecs.provider.api.IProvider;
import easycloud.ecs.provider.api.ProviderException;
import easycloud.ecs.provider.model.IStorageObject;
import easycloud.ecs.provider.model.IStorageObjectMetadata;
import easycloud.ecs.provider.model.snda.SndaStorageObject;
import easycloud.ecs.provider.model.snda.SndaStorageObjectMetadata;
import easycloud.ecs.provider.service.SendLogService;

public class SndaCloudStorageProvider implements IProvider {

	private Provider config;

	private SendLogService sendLogService;

	private static CSService adapter;

	private boolean isReady = false;

	private static final Log logger = LogFactory
			.getLog(SndaCloudStorageProvider.class);

	private static String BUCKET_NAME;

	public SndaCloudStorageProvider(Provider config) {
		this.config = config;
		SndaCloudStorageProvider.adapter = new RestCSService(
				new SNDACredentials(this.config.getAccessKey(),
						this.config.getSecretKey()));
		SndaCloudStorageProvider.BUCKET_NAME = this.config.getBucketName();
	}

	/**
	 * 
	 */
	@Override
	public boolean delete(Item item) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		try {
			SndaCloudStorageProvider.adapter.deleteObject(
					SndaCloudStorageProvider.BUCKET_NAME, item.getPath());
			result = true;
			SndaCloudStorageProvider.logger.info(getName() + " delete OK: "
					+ item.getPath());
		} catch (Exception e) {
			SndaCloudStorageProvider.logger.warn(getName() + " delete failed",
					e);
			throw new ProviderException(getName() + " delete failed", e,
					item.getPath());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.DELETE);
			log.setCreateTime(new Date());
			log.setResult(result);
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		return result;
	}

	@Override
	public URL getAccessURL(Item item) {
		Date expiration = new Date(System.currentTimeMillis()
				+ (1000 * 60 * 10));
		String url = SndaCloudStorageProvider.adapter.createSignedGetUrl(
				SndaCloudStorageProvider.BUCKET_NAME, item.getPath(),
				expiration);
		URL result = null;
		try {
			result = new URL(url);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 
	 * @param key
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObject getItem(Item item) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		StorageObject object = null;
		try {
			object = SndaCloudStorageProvider.adapter.getObject(
					SndaCloudStorageProvider.BUCKET_NAME, item.getPath());
			SndaCloudStorageProvider.logger.info(getName() + " getFile OK: "
					+ item.getPath());
			result = true;
		} catch (Exception e) {
			SndaCloudStorageProvider.logger.warn(getName() + " getFile failed",
					e);
			throw new ProviderException(getName() + " getFile failed", e,
					item.getPath());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.GET);
			if (object != null) {
				log.setLength(object.getContentLength());
			}
			log.setCreateTime(new Date());
			log.setResult(result);
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		if (object != null) {
			return new SndaStorageObject(object, item);
		} else {
			return null;
		}
	}

	private String getLogicalPath(CSObject summary, Folder folder) {
		String prefix = getPhysicalPath(folder);
		return StringUtils.replaceOnce(summary.getKey(), prefix,
				folder.getName());
	}

	@Override
	public String getName() {
		return config.getName();
	}

	private String getPhysicalPath(Folder folder) {
		for (Sync sync : config.getSyncs()) {
			if (sync.getFolder().equals(folder)) {
				return sync.getPath();
			}
		}
		return "";
	}

	/**
	 * 
	 */
	@Override
	public IStorageObjectMetadata head(Item item) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		StorageObject result = null;
		try {
			result = SndaCloudStorageProvider.adapter.headObject(
					SndaCloudStorageProvider.BUCKET_NAME, item.getPath());
			SndaCloudStorageProvider.logger.info(getName() + " head OK: "
					+ item.getPath());
		} catch (Exception e) {
			SndaCloudStorageProvider.logger.warn(getName() + " head failed", e);
			throw new ProviderException(getName() + " head failed", e,
					item.getPath());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.HEAD);
			log.setCreateTime(new Date());
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		if (result != null) {
			return new SndaStorageObjectMetadata(result, item);
		} else {
			return null;
		}
	}

	/**
	 * 
	 */
	@Override
	public List<IStorageObjectMetadata> list(Folder folder)
			throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		CSObject[] listing = null;
		try {
			String physicalPath = getPhysicalPath(folder);
			listing = SndaCloudStorageProvider.adapter.listObjects(
					SndaCloudStorageProvider.BUCKET_NAME, physicalPath, "/");
			SndaCloudStorageProvider.logger.info(getName() + " list OK: "
					+ folder.getName());
		} catch (Exception e) {
			SndaCloudStorageProvider.logger.warn(getName() + " list failed", e);
			throw new ProviderException(getName() + " list failed", e,
					folder.getName());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.LIST);
			log.setCreateTime(new Date());
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		if (listing != null) {
			List<IStorageObjectMetadata> result = new ArrayList<IStorageObjectMetadata>();
			for (CSObject summary : listing) {
				Item item = new Item();
				String path = getLogicalPath(summary, folder);
				item.setPath(path);
				item.setFolder(folder);
				result.add(new SndaStorageObjectMetadata(summary, item));
			}
			return result;
		} else {
			return null;
		}
	}

	/**
	 * 
	 * @param file
	 * @param key
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObjectMetadata putItem(Item item, InputStream is, long length)
			throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		StorageObject storageObject = null;
		StorageObject putResult = null;
		if (length > 0) {
			storageObject = new CSObject(item.getPath());
			storageObject.setDataInputStream(is);
			storageObject.setContentLength(length);
			try {
				putResult = SndaCloudStorageProvider.adapter.putObject(
						SndaCloudStorageProvider.BUCKET_NAME, storageObject);
				SndaCloudStorageProvider.logger.info(getName()
						+ " putFile OK: " + item.getPath());
				result = true;
			} catch (Exception e) {
				SndaCloudStorageProvider.logger.warn(getName()
						+ " putFile failed", e);
				throw new ProviderException(getName() + " putFile failed", e,
						item.getPath());
			} finally {
				easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
				log.setLogType(LOG_TYPE.PUT);
				log.setLength(length);
				log.setCreateTime(new Date());
				log.setResult(result);
				log.setProvider(config);
				sendLogService.addLog(log);
			}
		}
		return new SndaStorageObjectMetadata(putResult, item);
	}

	@Override
	public boolean ready() {
		if (isReady) {
			return true;
		}

		CSBucket bucket = SndaCloudStorageProvider.adapter
				.getOrCreateBucket(SndaCloudStorageProvider.BUCKET_NAME);
		isReady = bucket != null;
		return isReady;
	}

	@Override
	public void setSendLogService(SendLogService sendLogService) {
		this.sendLogService = sendLogService;
	}

}
